<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <style>
      .outer {
        width: 800px;
        height: 600px;
        border: 1px solid red;
        margin: 0 auto;
        position: relative;
      }

      .drag-box {
        width: 400px;
        height: 250px;
        position: absolute;
        background-image: url('https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg');
        background-size: 100%;
      }
    </style>

    <div class="outer">
      <div class="drag-box"></div>
    </div>

    <script type="text/javascript">
      var dragBox = document.getElementsByClassName('drag-box')[0];
      var oOuter = document.getElementsByClassName('outer')[0];

      // 是否处于拖拽状态，否的话不作任何处理
      var isDragging = false;
      // 鼠标按下时的原始位置
      var pointPosition = { x: 0, y: 0 };
      // dragBox的原始位置（让它在这基础上移动鼠标移动的距离）
      var dragBoxPosition = { x: 0, y: 0 };

      dragBox.addEventListener('mousedown', function (e) {
        isDragging = true;
        // 记录左键按下时，鼠标相对于浏览器可视区域的距离
        pointPosition.x = e.clientX;
        pointPosition.y = e.clientY;
        // 记录dragBox相对于outer的left和top（注意是绝对定位的left和top），转化为number类型
        dragBoxPosition.x = parseFloat(dragBox.style.left || 0);
        dragBoxPosition.y = parseFloat(dragBox.style.top || 0);
      });

      // 值得注意的是，mousemove的事件处理函数，是绑定在document中的，
      // 因为鼠标拖动速度太快时，有那么一瞬间，dragBox是跟不上鼠标的速度，
      // 鼠标会处于dragBox外面，甚至出于outer外面，
      // 所以这个事件处理函数需要由document来绑定
      document.addEventListener('mousemove', function (e) {
        // 当拖拽模式关闭时，什么都不执行
        if (!isDragging) return;

        // 计算鼠标相对于mousedown时，在x，y轴分别移动的距离
        var changeX = e.clientX - pointPosition.x;
        var changeY = e.clientY - pointPosition.y;
        // 让dragBox的style.left和style.top也移动鼠标移动的距离
        dragBox.style.left = dragBoxPosition.x + changeX + 'px';
        dragBox.style.top = dragBoxPosition.y + changeY + 'px';

        // 上述操作已完成拖拽，下面的操作是防止拖拽的div越过外层盒子
        if (dragBox.offsetLeft < 0) dragBox.style.left = 0;
        if (dragBox.offsetTop < 0) dragBox.style.top = 0;
        // 下面两个判断分别是防止dragBox右边界和上边界越界，可以将代码跑起来理解
        if (dragBox.offsetLeft > oOuter.offsetWidth - dragBox.offsetWidth) {
          dragBox.style.left = oOuter.offsetWidth - dragBox.offsetWidth + 'px';
        }
        // if (dragBox.offsetTop > oOuter.offsetHeight - dragBox.offsetHeight) {
        //   dragBox.style.top = oOuter.offsetHeight - dragBox.offsetHeight + 'px';
        // }
      });
      // 值得注意的是，mouseup的事件处理函数，是绑定在document中的，
      // 因为用户在拖动结束时，鼠标可能会处于outer之外，
      // 所以这个事件处理函数需要由document来绑定
      document.addEventListener('mouseup', function (e) {
        isDragging = false;
      });
    </script>
  </body>
</html>
